<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <meta name="generator" content="pandoc" />
  <title>nfs调试方法</title>
  <style type="text/css">code{white-space: pre;}</style>
  <style type="text/css">
div.sourceCode { overflow-x: auto; }
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
  margin: 0; padding: 0; vertical-align: baseline; border: none; }
table.sourceCode { width: 100%; line-height: 100%; }
td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
td.sourceCode { padding-left: 5px; }
code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
code > span.dt { color: #902000; } /* DataType */
code > span.dv { color: #40a070; } /* DecVal */
code > span.bn { color: #40a070; } /* BaseN */
code > span.fl { color: #40a070; } /* Float */
code > span.ch { color: #4070a0; } /* Char */
code > span.st { color: #4070a0; } /* String */
code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
code > span.ot { color: #007020; } /* Other */
code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
code > span.fu { color: #06287e; } /* Function */
code > span.er { color: #ff0000; font-weight: bold; } /* Error */
code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
code > span.cn { color: #880000; } /* Constant */
code > span.sc { color: #4070a0; } /* SpecialChar */
code > span.vs { color: #4070a0; } /* VerbatimString */
code > span.ss { color: #bb6688; } /* SpecialString */
code > span.im { } /* Import */
code > span.va { color: #19177c; } /* Variable */
code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code > span.op { color: #666666; } /* Operator */
code > span.bu { } /* BuiltIn */
code > span.ex { } /* Extension */
code > span.pp { color: #bc7a00; } /* Preprocessor */
code > span.at { color: #7d9029; } /* Attribute */
code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
  </style>
  <link rel="stylesheet" href="doc/linux-style.css" type="text/css" />
</head>
<body>
<div id="header">
<h1 class="title">nfs调试方法</h1>
</div>
<div id="TOC">
<ul>
<li><a href="#nfs调试方法">nfs调试方法</a><ul>
<li><a href="#nfs日志开关">nfs日志开关</a></li>
<li><a href="#tcpdump网络数据抓包">tcpdump网络数据抓包</a></li>
<li><a href="#nfs卸载">nfs卸载</a></li>
<li><a href="#nfs非特权源端口挂载">nfs非特权源端口挂载</a></li>
</ul></li>
</ul>
</div>
<h1 id="nfs调试方法">nfs调试方法</h1>
<h2 id="nfs日志开关">nfs日志开关</h2>
<p>nfs出错的情况下，大部分发行版会将错误的日志信息放到<code>/var/log/messages*</code>文件中，所以有些时候如何去获取一些调试信息，那么我们应该打开nfs和rpc的调试开关。 打开全部调试开关如下所示：</p>
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="bu">echo</span> 0xFFFF <span class="op">&gt;</span> /proc/sys/sunrpc/nfs_debug <span class="co"># NFSDBG_ALL</span>
<span class="bu">echo</span> 0x7fff <span class="op">&gt;</span> /proc/sys/sunrpc/rpc_debug <span class="co"># RPCDBG_ALL</span>
<span class="bu">echo</span> 0x7FFF <span class="op">&gt;</span> /proc/sys/sunrpc/nfsd_debug <span class="co"># NFSDDBG_ALL</span>
<span class="bu">echo</span> 0x7fff <span class="op">&gt;</span> /proc/sys/sunrpc/nlm_debug <span class="co"># NLMDBG_ALL</span></code></pre></div>
<p>这几个命令分别起到的作用是：启用 NFS 客户端的所有调试信息、用 RPC 核心层的全量调试信息、启用 NFS 服务器守护进程 nfsd 的所有调试信息、启用nlm的调试信息（nfsd覆盖服务端的文件操作，权限校验和锁管理，nlm是文件锁申请和释放，锁冲突和恢复）</p>
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="bu">echo</span> 0x0008 <span class="op">&gt;</span> /proc/sys/sunrpc/nfs_debug <span class="co"># NFSDBG_PAGECACHE   NFS 客户端页面缓存</span>
<span class="bu">echo</span> 0x0040 <span class="op">&gt;</span> /proc/sys/sunrpc/rpc_debug <span class="co"># RPCDBG_SCHED       RPC 任务调度器</span>
<span class="bu">echo</span> 0x0400 <span class="op">&gt;</span> /proc/sys/sunrpc/nfsd_debug <span class="co"># NFSDDBG_PNFS      NFS 服务器端并行 NFS</span>
<span class="bu">echo</span> 0x0008 <span class="op">&gt;</span> /proc/sys/sunrpc/nlm_debug <span class="co"># NLMDBG_SVCLOCK     NLM 服务端文件锁管理 </span></code></pre></div>
<p>上述这些命令则是只打开某个相关的debug，具体的那一些位掩码的数值对应哪个调试操作，可以查看源码的头文件定义。 <a href="https://elixir.bootlin.com/linux/v6.15/source">在线内核源码阅读网站</a></p>
<h2 id="tcpdump网络数据抓包">tcpdump网络数据抓包</h2>
<p>使用网络抓包可以直观的看到在端到端的客户端请求和服务端请求，时间精度也很高，所以掌握抓包的方式也很重要。</p>
<p>抓包示例：</p>
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># --interface: 指定要监听的网络接口，any表示所有的网络接口</span>
<span class="co"># --buffer-size: 默认4KB, 单位 KB, 20480 代表 20MB。buffer大一点可以防止抓包数据丢失</span>
<span class="ex">tcpdump</span> --interface=<span class="op">&lt;</span>网络接口<span class="op">&gt;</span> --buffer-size=20480 -w out.cap</code></pre></div>
<p>当数据量比较大时，有时会发生抓包数据丢失。配置网络参数，把参数调大可以防止抓包数据丢失:</p>
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">sysctl</span> -a <span class="kw">|</span> <span class="fu">grep</span> net.core.rmem <span class="co"># 查看配置</span>
<span class="ex">sysctl</span> net.core.rmem_default=xxx
<span class="ex">sysctl</span> net.core.rmem_max=xxx</code></pre></div>
<p>三条命令分别用于：查看缓冲区的配置大小、临时修改默认接收缓冲区大小、临时修改最大接收缓冲区大小</p>
<p>使用<a href="https://www.wireshark.org/">wireshark</a>对抓取的数据进行分析，如果要查看端口，需要在<code>preferences -&gt; appearance -&gt; columns</code>中添加<code>Src port (unresolved)</code>和<code>Dest port</code></p>
<h2 id="nfs卸载">nfs卸载</h2>
<p>在使用<code>umount</code>命令的时候可能会出现<code>device is busy</code>的问题，这其实就说明挂载点正在使用，那么需要我们找到使用挂载点的进程。如何查找使用挂载点的进程。命令如下：</p>
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># +D：递归地列出指定目录下所有打开的文件</span>
<span class="ex">lsof</span> +D <span class="op">&lt;</span>挂载点<span class="op">&gt;</span>
<span class="co"># -m：表示查询挂载点（而不仅仅是某个文件）</span>
<span class="fu">fuser</span> -m <span class="op">&lt;</span>挂载点<span class="op">&gt;</span></code></pre></div>
<p>打印出的信息主要是包括挂载点和pid进程，可以看到是哪些进程正在使用挂载点，直接使用<code>sudo kill -9 &lt;pid&gt;</code>杀死进程号。</p>
<p>有些情况下不能直接kill正在使用的进程，可以使用以下命令延迟卸载，会导致挂载点在后台被卸载，而不会强制终止进程：</p>
<p><code>umount --lazy &lt;挂载点&gt;</code></p>
<h2 id="nfs非特权源端口挂载">nfs非特权源端口挂载</h2>
<p>首先解释一下这个非特权端口是什么意思？在 NFS 挂载中，&quot;非特权源端口&quot; 指的是客户端发起 NFS 连接时使用的 源端口号大于 1023 的端口。 小于1023的端口都需要root权限，例如SSH(22)、HTTP(80)。而大于1023的端口普通用户即可，对于nfs来说普遍是2049。当然这是指的客户端(client)，服务端的端口都是小于1024的。</p>
<p>但在某些情况下，无法挂载时，可以尝试使用大于1024的非特权端口挂载，这对排查问题很有帮助。如何配置呢？之前有在搭建nfs-server环境的时候配置过/etc/exports文件下对导出路径增加insecure选项，如: <code>/tmp/ *(rw,no_root_squash,fsid=0,insecure)</code></p>
<p>保存重启server，这个时候server端默认还是使用小于1024的端口，选择大于1024的端口需要挂载选项noresvport: <code>mount -t nfs -o noresvport ${server_ip}:/ /mnt</code></p>
</body>
</html>
